package com.mokairui.example.chathome;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * @author Mokairui
 * @Description 服务端群聊系统实现
 * @Since 2021/6/30
 */
public class Server {
    // 1.定义一些成员属性：选择器，服务端通道，端口
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private static final int PORT = 9999;

    // 2.定义初始化代码逻辑
    public Server() {
        try {
            // 2.1创建选择器对象
            selector = Selector.open();
            // 2.2获取通道
            serverSocketChannel = ServerSocketChannel.open();
            // 2.3绑定客户连接的端口
            serverSocketChannel.bind(new InetSocketAddress(PORT));
            // 2.4设置非阻塞通信模式
            serverSocketChannel.configureBlocking(false);
            // 2.5把通道注册到选择器上，并且开始指定接受事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 创建服务端对象
        Server server = new Server();
        // 开始监听客户端的各种消息事件：连接，群聊消息，离线消息
        server.listen();
    }

    /**
     * 开始监听
     */
    private void listen() {
        while (true) {
            try {
                if (!(selector.select() > 0)) break;
                // 获取选择器中所有注册通道的就绪事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    // 提取这个事件
                    SelectionKey next = iterator.next();
                    // 判断这个事件的类型
                    if (next.isAcceptable()) {
                        // 客户端接入请求
                        // 获取当前用户的通道
                        SocketChannel accept = serverSocketChannel.accept();
                        // 设置为非阻塞模式
                        accept.configureBlocking(false);
                        // 注册给选择器，监听读数据事件
                        accept.register(selector, SelectionKey.OP_READ);
                    } else if (next.isReadable()) {
                        // 处理这个客户端的消息，接受它然后实现转发逻辑
                        readClientData(next);
                    }
                    iterator.remove(); // 移除已经处理的事件
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 接受当前客户端通道的信息，转发给其他客户端通道
     * @param next
     */
    private void readClientData(SelectionKey next) {
        SocketChannel socketChannel = null;
        try {
            socketChannel = (SocketChannel) next.channel();
            // 创建缓冲区对象开始接受客户端通道的数据
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = socketChannel.read(buffer);
            if (count > 0) {
                buffer.flip();
                String msg = new String(buffer.array(), 0, count);
                System.out.println("接收到了客户端消息：" + msg);
                // 把这个消息推送给全部客户端接受
                sendMessageToClient(msg, socketChannel);
            }
        } catch (Exception e) {
            try {
                System.out.println("有人离线了" + socketChannel.getRemoteAddress());
                // 当前客户端离线
                next.cancel();
                socketChannel.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    /**
     * 把当前客户端的消息数据推送给当前全部在先注册的channel
     * @param msg
     * @param socketChannel
     */
    private void sendMessageToClient(String msg, SocketChannel socketChannel) throws IOException {
        System.out.println("服务端开始转发这个消息：当前处理的线程：" + Thread.currentThread().getName());
        for (SelectionKey key : selector.keys()) {
            SelectableChannel channel = key.channel();
            // 不要把数据发给自己
            if (channel instanceof SocketChannel && socketChannel != channel) {
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                socketChannel.write(buffer);
            }
        }
    }
}
